using System;
using System.Collections.Generic;
using System.Text;
using Sedodream.Sudoku.Board;
using Sedodream.Sudoku.Evaluators;
namespace Sedodream.Sudoku.Players
{
	public class ForcedMovesPlayer : IPlayer
	{
		#region Fields
		private int numMovesPlayed = 0;
		private int maxNumMovesPlayed;
		private MoveScoreComparer moveComparer = new MoveScoreComparer();
		private IEvaluator evaluator;
		#endregion

		#region Constructors
		public ForcedMovesPlayer()
		{
			numMovesPlayed = 0;
			maxNumMovesPlayed = int.MaxValue;
			moveComparer = new MoveScoreComparer();
			evaluator = new SimpleEvaluator();
		}
		#endregion

		private IEvaluator Evaluator
		{
			get { return this.evaluator; }
			set { this.evaluator = value; }
		}
		private MoveScoreComparer MoveComparer
		{
			get { return this.moveComparer; }
			set { this.moveComparer = value; }
		}

		#region IPlayer Members
		public int MaxNumMoves
		{
			get { return this.maxNumMovesPlayed; }
			set { this.maxNumMovesPlayed = value; }
		}
		public int NumMovesPlayed
		{
			get { return this.numMovesPlayed; }
		}
		public IBoard StartPlay(IBoard board)
		{
			IBoard solvedBoard = null;
			bool solved = AdvanceBoard(board, out solvedBoard);
			if (solved)
			{
				int debug = 33;
			}
			return solvedBoard;
		}

		#endregion
		private bool AdvanceBoard(IBoard board, out IBoard solvedBoard)
		{
			solvedBoard = null;
			board.UpdateBoardState();
			if (board.State == BoardState.Solved)
			{
				return true;
			}
			if (board.State == BoardState.Failed)
			{
				return false;
			}
			List<IMove> moveList = board.FindAllMoves();
			if (moveList == null || moveList.Count <= 0)
			{
				return false;
			}

			int numForcedMoves = 0;
			//cycle through and play all forced moves
			IList<IMove> forcedMovesPlayed = new List<IMove>();
			bool hasForcedMoves = true;
			while (hasForcedMoves)
			{
				foreach (IMove move in moveList)
				{
					if (move.ForcedMove)
					{
						numForcedMoves++;
						BoardState bSstate = PlayMove(board, move);
						forcedMovesPlayed.Add(move);
						if (bSstate == BoardState.Solved)
						{
							solvedBoard = board;
							return true;
						}
						if (bSstate == BoardState.Failed)
						{
							//							board.UpdateBoardState();
							string stateMessage = string.Format("Invalid state due to playing forced move");
							return false;
							//throw new ApplicationException(stateMessage);
						}
					}
					else
					{
						//moves should be sorted, forced moves first
						break;
					}
				}
				foreach (IMove move in forcedMovesPlayed)
				{
					if (moveList.Contains(move))
						moveList.Remove(move);
				}
				forcedMovesPlayed.Clear();
				moveList = board.FindAllMoves();
				if (moveList != null && moveList.Count > 0 && moveList[0] != null && moveList[0].ForcedMove)
					hasForcedMoves = true;
				else
					hasForcedMoves = false;
			}

			board.UpdateBoardState();
			BoardState state = board.State;
			if (state == BoardState.Solved)
			{
				solvedBoard = board;
				return true;
			}
			if (state == BoardState.Failed)
			{
				string stateMessage = string.Format("Invalid state due to playing forced move");
				throw new ApplicationException(stateMessage);
			}
			//TODO: Remove failing moves
			if (moveList != null && moveList.Count > 0)
			{
				//now we have to score each move
				foreach (IMove move in moveList)
				{
					move.MoveScore = Evaluator.Evaluate(this, move,moveList);
					if (move.MoveScore.MajorScore == double.MinValue &&
						move.MoveScore.MinorScore == double.MinValue)
					{
						//this is a failing move & should be removed
						int remove = 3;
					}
					int debug = 3;
				}
				//now we need to sort the moves
				moveList.Sort(moveComparer);
			}
			foreach (IMove move in moveList)
			{
				//IBoard newBoard = new SudokuBoard((IBoardData)board.BoardData.Clone());

				//first see if the move is still valid
				if (!board.IsMoveValid(move))
					continue;
				BoardState newState = PlayMove(board, move);

				if (newState == BoardState.Solved)
				{
					return true;
				}
				if (AdvanceBoard(board, out solvedBoard) == true)
				{
					if (solvedBoard == null)
						solvedBoard = board;
					return true;
				}
				this.UndoMove(board, move);
			}
			if (board.State == BoardState.Solved)
			{
				int debug = 3;
			}
			return (board.State == BoardState.Solved);
		}
		public void UndoMove(IBoard board, IMove move)
		{
			if (board == null)
				throw new ArgumentNullException("board");
			if (move == null)
				throw new ArgumentNullException("move");
			board.SetValueAt(move.Location, null);
			//board.Cells[move.Location.Row,move.Location.Col].Cell
			//TODO: Validate that this move has been played on the board

		}
		public BoardState PlayMove(IBoard board, IMove move)
		{
			if (board == null)
				throw new ArgumentNullException("board");
			if (move == null)
				throw new ArgumentNullException("move");

			board.SetValueAt(move.Location, move.Value);
			board.UpdateBoardState();
			if (board.State == BoardState.Failed)
			{
				int debug = 3;
			}
			numMovesPlayed++;
			if (numMovesPlayed == 1000)
			{
				DateTime end = DateTime.Now;
				int debug = 33;
			}
			if (board.State == BoardState.Solved)
			{
				int debug2 = 33;

			}
			return board.State;
		}
	}
}
